#include "memmanager.h"
#include "translate.h"
#include "machine.h"
#include "synch.h"
#include "syscall.h"
#include "filesys.h"
//#include "main.h"

///////////////////////////////////////////////////////////////////
//     CoreFreeMap: A frame is said to be free only if it does not belong
//     the page buffer, and does not belong to any process.
//
//     CoreOwners: If a frame has the contents of some process's page
//     then its entry in core owners points to the corresponding page
//     table entry.
///////////////////////////////////////////////////////////////////

//MemManager::MemManager( int pbuffer_in , int hbits_in )
MemManager::MemManager()
{

    // WARNING: coreFreeMap (and like wise swapFreeMap) bits are CLEAR 
    // when free, not set when free; 
    coreFreeMap = new Bitmap(NumPhysPages);  
    coreOwners = new TranslationEntry* [NumPhysPages];

    swapFreeMap =  new Bitmap(NumSwapPages);
    swapValidMap = new Bitmap(NumSwapPages);
    swapOwners =   new TranslationEntry* [NumSwapPages];


    int physFrame;
    for (physFrame=0; physFrame<NumPhysPages; physFrame++)
      coreOwners[physFrame] = (TranslationEntry*)NULL;

    int swapFrame;
    for (swapFrame=0; swapFrame<NumSwapPages; swapFrame++)
      swapOwners[swapFrame] = (TranslationEntry*)NULL;

    mutex=new Semaphore("mutex for memory manager data structures",1);

    ASSERT(kernel->fileSystem->Create("nachos.bs"));  // must be able to create...
    swapfile=kernel->fileSystem->Open("nachos.bs");
    ASSERT(swapfile!=NULL);                     // ...and open the swapfile

    buffersize = 0;
   //
   // MP3 CODE
   //
}

#include <errno.h>

//////////////////////////////////////////////////////////////////////
// Destructor: free all the resource used by MemManager
//////////////////////////////////////////////////////////////////////
MemManager::~MemManager()
{
    DEBUG(dbgAddr, "MemManager destroyed with: " << memAvail() << " free pages\n" );
    delete coreFreeMap;
    delete swapFreeMap;
    delete [] coreOwners;
    delete [] swapOwners;
    delete swapValidMap;
    delete swapfile;  //close it, then remove it:
    kernel->fileSystem->Remove("nachos.bs");
    delete [] pageBuffer;

   //
   // Your CODE 
   //
}


unsigned int MemManager::memAvail()
{
    return ( coreFreeMap->NumClear()+swapFreeMap->NumClear() );
}
 

void MemManager::clear(TranslationEntry *pageTable, int numPages)
{
  int swapFrame;

  for (int i = 0; i < numPages; i ++)
    if (pageTable[i].legal)
    {
      if (pageTable[i].valid) {
	// Free physical frames
	coreFreeMap->Clear(pageTable[i].physicalPage);
	coreOwners[pageTable[i].physicalPage] = (TranslationEntry *) NULL;
      }
      else if ((swapFrame = swapSearch(&pageTable[i])) != -1)
      {
	  // Free swap frames.
	  swapFreeMap->Clear(swapFrame);
	  swapValidMap->Clear(swapFrame);
	  swapOwners[swapFrame]=(TranslationEntry*)NULL;
      }
    }
}
 
//////////////////////////////////////////////////////////////////////
//  Finds a free frame in the main memory. See the definition above of 
//  a free frame.
//////////////////////////////////////////////////////////////////////
int MemManager::locateFirst()
{
    return coreFreeMap->FindAndSet();
}

//////////////////////////////////////////////////////////////////////
// MP3: Include your code here
// Make a frame free using the enhanced second chance page replacement 
// algorithm and N bit LRU replacement.
//////////////////////////////////////////////////////////////////////
int MemManager::makeFreeFrame() 
{
  //
  // Your CODE 
  //
  static int counter;
  counter = ( counter + 1 ) % NumPhysPages;  // Trivial revolving counter.

  return( counter );
}
  
  
//////////////////////////////////////////////////////////////////////
// Include your code here
// Implements the page buffering policy described above
//////////////////////////////////////////////////////////////////////
void MemManager::faultIn(TranslationEntry *PTEntry) 
{
  //
  // Your CODE 
  //
  
  int physPage = coreFreeMap->FindAndSet();
  if( physPage >= 0 )
    pageIn( PTEntry, physPage );
  else {
    physPage = makeFreeFrame();
    if( coreOwners[physPage]->dirty ) 
      pageOut( physPage );
    else 
      coreOwners[physPage]->valid = FALSE;
    
    pageIn( PTEntry, physPage );
    
  }
}

////////////////////////////////////////////////////
// Interrupt Service routine for maintaining history data
// Your code here.
//  Note: arg is a dummy argument that is meaningless and should not be used.
/////////////////////////////////////////////////////
void MemManager::RecordHistory( int arg )
{
  //
  // Your CODE 
  //
}


/////////////////////////////////////////////////////////////////
// Reads in the appropriate page into the physFrame mentioned
/////////////////////////////////////////////////////////////////
void MemManager::pageIn(TranslationEntry *PTEntry, int physFrame)
{
  int  swapFrame;
  int  x;
  char my_buffer[PageSize];

  // Search swap file for PTEntry.
  swapFrame = swapSearch( PTEntry );

  if( swapFrame >= 0 )
  { // Frame found.  Read it in from BS to temporary buffer.
    swapfile->ReadAt( my_buffer, PageSize, PageSize * swapFrame);
  }
  else
  { // Frame not found.  Read it from the source file.
    kernel->currentThread->space->ReadSourcePage( my_buffer, PTEntry->virtualPage);
  }

  PTEntry->physicalPage = physFrame;
  PTEntry->valid = TRUE;
  coreOwners[physFrame] = PTEntry;

  for( x=0; x < PageSize; x++ )
    kernel->machine->WriteMem( PTEntry->virtualPage * PageSize + x, 1, (int)my_buffer[x] );

  PTEntry->use   = FALSE;
  PTEntry->dirty = FALSE;

  // Your CODE : Remember to adjust the history appropriately.

  return;
}


/////////////////////////////////////////////////////////////////
// Reads the appropriate page into a swap frame. 
/////////////////////////////////////////////////////////////////
void MemManager::pageOut(int physFrame) 
{
  TranslationEntry * victim_te = (TranslationEntry *)NULL;
  int  my_buffer[PageSize];
  char cbuf[PageSize];
  int  swapFrame;
  int  check;
  int  x;

  victim_te = coreOwners[ physFrame ];

  // Copy memory contents to temporary kernel buffer.
  // Make valid so translation works.
  victim_te->valid = TRUE;
  for( x = 0; x < PageSize ; x++ )
    kernel->machine->ReadMem( victim_te->virtualPage * PageSize + x, 1, my_buffer+x );
  victim_te->valid = FALSE;

  // Convert Int array to char array.  This avoids endian problems.
  for( x = 0; x < PageSize; x++ )
    cbuf[x] = (char)(my_buffer[x]);

  // Search for previously owned page in the swap file.
  for( swapFrame = 0;
       ( (swapFrame < NumSwapPages) && (victim_te != swapOwners[swapFrame]) );
       swapFrame++ );

  if( swapFrame != NumSwapPages )
    { // Previous page found.
      swapFreeMap->Mark(swapFrame); // deadbeef
    }
  else
    { // Previous page not found in swap file, so allocate one.
      swapFrame = swapFreeMap->FindAndSet();
      ASSERT( swapFrame >= 0 );
      swapOwners[ swapFrame ] = victim_te;
    }

  // Write Page to Backing store, because always dirty. 
  check = swapfile->WriteAt( cbuf, PageSize, PageSize * swapFrame );
  ASSERT( check == PageSize );

  // Clear dirty bit.
  victim_te->dirty = FALSE;

  // It may or may not have been changed by the paging scheme, so just for 
  //  good measure.
  victim_te->valid = FALSE;

  return;
}


void MemManager::PageFaultExceptionHandler(int BadVPage) 
{
  mutex->P();

  if (BadVPage >= kernel->machine->pageTableSize || kernel->machine->pageTable[BadVPage].legal == FALSE)
  {
    printf("Illegal memory access by thread : %s\n", kernel->currentThread->getName());
    printf("Halting the thread : %s\n", kernel->currentThread->getName());

    mutex->V();
    // call SC_Exit syscall exception
    kernel->machine->WriteRegister(2, SC_Exit);
    // The exit status
    kernel->machine->WriteRegister(4, 0);
    ExceptionHandler(SyscallException);
    // The current execution of this method will never get beyond this
    // position. The thread in whose context the method is executing is
    // halted. This is the reason why the mutex is released right here.
  }

  printf("Paging in virtual page %d\n", BadVPage);
  faultIn(kernel->machine->pageTable+BadVPage);
  
  mutex->V();
}


int MemManager::swapSearch(TranslationEntry *PTEntry) 
{
  int swapFrame;

  for (swapFrame=0; swapFrame<NumSwapPages; swapFrame++)
    if (swapOwners[swapFrame]==PTEntry) 
	return swapFrame;

  return -1;
}

    

// Actual interrupt handler.  Uses divider to lower overhead.
void PageTimer ( int arg )
{
   static int counter;
   int divider = 64;
   
   if ( !(counter = (++counter % divider ))) {
      kernel->machine->memManager->RecordHistory( arg );
   }
   return;
}
